#include <cmath>
#include "gui/layers/DynamicLayer.h"
#include "gui/util/GUIObjectDrawer.h"
#include "util/LinkedList.h"

namespace gui {

DynamicLayer::DynamicLayer(): Layer() {

}

void DynamicLayer::updateOffset(int x, int y) {
	this->offset.x = min(this->offset.x, x);
	this->offset.y = min(this->offset.y, y);
}

void DynamicLayer::updateSurface(GUIObjectsContainer objectsContainer) {
	// TODO ver guiMap->getLeftLimit() y guiMap->getTopLimit() para los position
	delete this->completeSurface;
	GUIObjectDrawer drawer;
	util::LinkedList<GUIObject>::Iterator it = objectsContainer.getGUIObjects().iterator();
	// Dibujo al primer objeto y seteo el offset de la superficie total como el offset del objeto.
	if (it.hasNext()) {
		GUIObject* current = it.next();
		this->completeSurface = drawer.drawGUIObject(current);
		this->updateOffset(current->getXPos() - this->completeSurface->getWidth() / 2, current->getYPos() - this->completeSurface->getHeight() / 2);
	}
	while (it.hasNext()) {
		// Dibujo cada objeto restante.
		GUIObject* current = it.next();
		Surface* surface = drawer.drawGUIObject(current);
		Portion position;
		position.x = current->getXPos() - surface->getWidth() / 2;
		position.y = current->getYPos() - surface->getHeight() / 2;

		// Calculo las nuevas dimensiones de la superficie completa.
		int newWidth = 0;
		if (this->offset.x < position.x && surface->getWidth() + position.x < this->offset.x + this->completeSurface->getWidth()) {
			// Si la nueva superficie esta inscripta dentro de la completa, las dimensiones de la superficie completa no cambian.
			newWidth = this->completeSurface->getWidth();
		} else if (position.x < this->offset.x) {
			// Si la nueva superficie esta en parte o totalmente a la izquierda de la completa, recalculo la dimension.
			newWidth = this->offset.x + this->completeSurface->getWidth() - position.x;
		} else {
			// Si la nueva superficie esta en parte o totalmente a la derecha de la completa, recalculo la dimension.
			newWidth = position.x + surface->getWidth() - this->offset.x;
		}

		// Lo mismo para "y".
		int newHeight = 0;
		if (this->offset.y < position.y && surface->getHeight() + position.y < this->offset.y + this->completeSurface->getHeight()) {
			newHeight= this->completeSurface->getHeight();
		} else if (position.y < this->offset.y) {
			newHeight = this->offset.y + this->completeSurface->getHeight() - position.y;
		} else {
			newHeight = position.y + surface->getHeight() - this->offset.y;
		}

		// Calculo el offset como el menor entre el x de la superficie completa y el de la nueva (lo mismo para y).
		int newOffsetX = min(this->offset.x, position.x);
		int newOffsetY = min(this->offset.y, position.y);

		// Redibujo.
		Surface* aux = this->completeSurface;
		this->completeSurface = new Surface(newWidth, newHeight, 0, 0, 0);
		// Calculo la posicion de la superficie completa anterior dentro de la nueva superficie completa.
		Portion completeSurfacePosition;
		completeSurfacePosition.x = this->offset.x - newOffsetX;
		completeSurfacePosition.y = this->offset.y - newOffsetY;
		this->completeSurface->applySurface(aux, completeSurfacePosition);
		delete aux;
		// Calculo la posicion de la nueva superficie dentro de la nueva superficie completa.
		Portion surfacePosition;
		surfacePosition.x = position.x - newOffsetX;
		surfacePosition.y = position.y - newOffsetY;
		this->completeSurface->applySurface(surface, surfacePosition);
		delete surface;
		this->updateOffset(newOffsetX, newOffsetY);
	}
}

}
